package edu.rit.varapp.utilities;

import java.lang.reflect.Method;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static edu.rit.varapp.utilities.StringUtils.emptyString;

/**
 * Various validation methods.
 * @author Eric Kisner
 * @author Dara Kordestani
 */
public class ValidationUtils {

    /**
     * This method uses reflection to run any validate method in this class.
     * @param inputName The name of the method to run. Since each validate method is named validateXXXX,
     *                  we only need to pass in the end of the method name. So, for example, to run validateEmail,
     *                  the call would be runValidateMethod( "email", emailString ).
     * @param value The value to pass into the validate method. Since each validate method requires only a string,
     *              we don't have to worry about passing in any other value.
     * @return All validate methods return a boolean (specifying whether the input is valid or not), so this method
     *         will just return the result of calling the validate method.
     */
    public static boolean runValidateMethod( String inputName, String value ) {
        for( Method method : ValidationUtils.class.getMethods() ) {
            if( method.getName().toLowerCase().equals( "validate" + inputName.toLowerCase() ) ) {
                try {
                    //Since the methods are all static, we do not need to get an instance of the ValidationUtils
                    //class - we can just invoke the method.
                    return (Boolean) method.invoke( null, value );
                } catch( Exception e ) {
                    ExceptionUtils.handleException( e, ValidationUtils.class );
                    //This will most likely never happen, since the validate methods have identical conventions,
                    //which means it would only not run if this method is used incorrectly, or any of the methods
                    //are moved.
                    System.exit( 1 );
                }
            }
        }
        return false;
    }

    /**
     * This method will take a list of method names, values to be passed into those methods, and messages to
     * display on an invalid value, and return an error message if there was an invalid value, or an empty string.
     *
     * To use this method, the method names, message, and values Lists must all match with each other. For example,
     * to validate an email and a number, the input would be:
     * methodNames = { "Email", "Number" };
     * messages = { "Email", [whatever the field of the number is labeled] };
     * values = { emailText, numberText };
     *
     * @param methodNames The names of the validate methods to run.
     * @param messages The error messages to display if any invalid values are found.
     * @param values The values to pass into the validate methods.
     * @return An error message saying if there was any invalid input, or an empty string.
     * @see ValidationUtils#runValidateMethod(java.lang.String, java.lang.String)
     */
    public static String runValidateMethodList( List<String> methodNames,
                                                List<String> messages,
                                                List<String> values ) {
        boolean invalidInputFound = false;
        String errorMessage = "Please enter a valid:\n";
        for( int i = 0; i < methodNames.size(); i++ ) {
            if( !runValidateMethod( methodNames.get( i ), values.get( i ) ) ) {
                errorMessage += "-" + messages.get( i ) + "\n";
                invalidInputFound = true;
            }
        }
        if( invalidInputFound ) {
            return errorMessage;
        } else {
            return emptyString();
        }
    }

    /**
     * Matches on xxxx@xxxx.com
     */
    public static boolean validateEmail( String inputEmail ) {
        boolean valid = false;
        String regExp = ".+@.+\\.[a-z]+";
        Pattern pattern = Pattern.compile( regExp, Pattern.CASE_INSENSITIVE );
        Matcher matcher = pattern.matcher( inputEmail );

        if( matcher.matches() == true || inputEmail.length() == 0 ) {
            valid = true;
        }
        return valid;
    }

    /**
     * Matches a number (and no other characters).
     */
    public static boolean validateNumber( String number ) {
        boolean valid = false;
        String regExp = "[0-9]+";
        Pattern checkPattern = Pattern.compile( regExp );
        Matcher matcher = checkPattern.matcher( number );

        if( matcher.matches() == true || number.length() == 0 ) {
            valid = true;
        }
        return valid;
    }

    /**
     * Validates characters and spaces.
     */
    public static boolean validateChar( String input ) {
        boolean valid = false;
        String regExp = "([a-zA-Z]\\\'?)+((\\s|\\-)([a-zA-Z]\\\'?)+)*";
        Pattern checkPattern = Pattern.compile( regExp );
        Matcher matcher = checkPattern.matcher( input );

        if( matcher.matches() == true || input.length() == 0 ) {
            valid = true;
        }
        return valid;
    }

    /**
     * Validates the following titles:
     * "Mr", "Mrs", "Miss", "Ms"
     *
     * Other titles can be added, however, due to this project's application, we didn't feel the need to add
     * all titles.
     */
    public static boolean validateTitle( String input ) {
        boolean valid = false;
        String regExp = "m(r|rs|iss|s)\\.?";
        Pattern checkPattern = Pattern.compile( regExp, Pattern.CASE_INSENSITIVE );
        Matcher matcher = checkPattern.matcher( input );
        //input = input.replaceAll( "\\.", "" );
        //input = input.toLowerCase();
        //if( input.length() == 0 || input.equals( "mr" ) || input.equals( "mrs" ) || input.equals( "miss" ) || input.equals( "ms" ) ) {
        if( matcher.matches() == true || input.length() == 0 ) {
            valid = true;
        }
        return valid;
    }

    /**
     * Validates a state code. Ex: CT, NY, CA
     */
    public static boolean validateState( String input ) {
        boolean valid = false;
        String regExp = "[A-Z][A-Z]";
        Pattern checkPattern = Pattern.compile( regExp );
        Matcher matcher = checkPattern.matcher( input );

        if( matcher.matches() == true || input.length() == 0 ) {
            valid = true;
        }
        return valid;
    }

    /**
     * Validates a datetime in the form of 2000-01-01 00:00:00.0
     */
    public static boolean validateDate( String date ) {
        boolean valid = false;
        String regExp = "(18|19|20)\\d{2}\\-(0[1-9]|1[0-2])\\-(0[1-9]|[12][0-9]|3[01])";
        Pattern checkPattern = Pattern.compile( regExp );
        Matcher matcher = checkPattern.matcher( date );

        if( matcher.matches() == true || date.length() == 0 ) {
            valid = true;
        }
        
        return valid;
    }

    /**
     * Validates a name in the form of John Smith with both first name and last name
     */
    public static boolean validateName (String input){
        boolean valid = false;
        String regExp = "[a-zA-Z]+(\\s[a-zA-Z]+)+";
        Pattern checkPattern = Pattern.compile( regExp );
        Matcher matcher = checkPattern.matcher( input );

        if( matcher.matches() == true) {
            valid = true;
        }
        return valid;

    }

    /**
     * Validates the length of varchar to make sure it is not null
     */
    public static boolean validateVarCharLength (String name){
        if (name.length() == 0){
            return false;
        }
        else{
            return true;
        }

    }

    /**
     * Validates the length of number to make sure it is not null and then sends
     * it to validateNumber()
     */
    public static boolean validateNumLength (String input){
        if (input.length() == 0){
            return false;
        }
        else{
            return validateNumber(input);
        }

    }
}
